//#include <tchar.h>
//#include <boost/asio/error.hpp>
//
//#include <boost/array.hpp> 
//#include <boost/asio/buffer.hpp>
//
//#include <vector>
//#include <iostream>

#include "server.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include "protocol.h"
//#include <string.h>
using namespace boost::asio; 

#define SETRED
#define SETGREEN
#define SETWHITE

#include <boost/date_time/posix_time/posix_time.hpp>
#define CURRENTTIME (microsec_clock::universal_time() - startpt).total_milliseconds();
/*
#define SETRED SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_INTENSITY);
#define SETGREEN SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_INTENSITY);
#define SETWHITE SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), \
	FOREGROUND_RED |\
	FOREGROUND_GREEN |\
	FOREGROUND_BLUE);
*/

/*
inline bool	IsKeyDown(int virtualKey)
{
	return (::GetAsyncKeyState(virtualKey) & 0x8000) == 0x8000;
}
*/
namespace NOEXIT
{
	CServer::CServer():sessionCount(0),bulletCount(0),GuaranteeSendFlag(false)
	{
		address = ip::udp::endpoint(ip::address::from_string("127.0.0.1"),2733);//"129.119.142.118" "127.0.0.1"
		serverSocket = new ip::udp::socket(service,address);
	}

	CServer::~CServer()
	{
		delete serverSocket;
	}

	void CServer::startReceive()	
	{
		serverSocket->async_receive_from(buffer(byteData,BUFFSIZE),client_address,
			boost::bind(&CServer::MsgHandler,this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred)
			);
	}

	void CServer::start()
	{
		startpt = microsec_clock::universal_time();
		long start = clock();//check the conn every 1s for clients
		//long displayConn = clock();
		long sendGuarantee= clock();
		long long now;

		startReceive();
		std::cout<<"start\n";
		std::cout<<sizeof(long)<<" "<<sizeof(SMsg)<<" "<<sizeof(PlayerInfo)<<" "<<sizeof(BulletInfo)<<"\n";
		while(1)
		{
			//Sleep(1);
			service.poll();

			// while messages in queue (do some processing)

			now = CURRENTTIME;
			if(now - start > 1000)
			{
				std::vector<Session>::iterator i;
				i = g_session_list.begin();
				for(;i != g_session_list.end(); )
				{
					(*i).connectionInfo.lostCount += 1;
					if((*i).connectionInfo.lostCount > 5)
					{
							std::cout<<"**client disconnected: session "<<(*i).sessionId<<"\n"<<
							(*i).connectionInfo.clientEndPoint.address().to_string()<<":"<<(*i).connectionInfo.clientEndPoint.port()<<std::endl;
							i = playerLeaveGame((*i).sessionId);
							//i = g_session_list.erase(i);
					}
					else
					{
						i++;
					}

				}
				start = now;//timing every sec
			}

			//collision
			long long bulletTime = CURRENTTIME
			
			for(std::list<BulletInfo>::iterator it = bullets_list.begin(); it!= bullets_list.end();)
			{
				bool hitflag = false;
				float bx = it->pos[0] + it->velocity[0] *  ((bulletTime - it->spawnTime)/16);
				float bz = it->pos[2] + it->velocity[2] *  ((bulletTime - it->spawnTime)/16);
				for(std::vector<Session>::iterator it_player = g_session_list.begin(); it_player != g_session_list.end();++it_player)
				{
					float px = it_player->pinfo.pos[0];
					float pz = it_player->pinfo.pos[2];
				
					if(it_player->pinfo.playerId != it->playerId &&
						(bx -  px)*(bx -  px) + (bz -  pz)*(bz -  pz) < 150)
					{
						
						it_player->pinfo.pos[0] = getRand(100,850);
						it_player->pinfo.pos[2] = getRand(100,850);
						hitflag = true;
						std::cout<<"hit "<<it_player->pinfo.playerId<<"\n";

						SMsg resetMsg,removeBulletMsg;
						resetMsg.msgId = RESETPLAYER;
						resetMsg.guarantee = true;
						memcpy(resetMsg.data,&it_player->pinfo,sizeof(PlayerInfo));
						resetMsg.dataSize = sizeof(PlayerInfo);

						removeBulletMsg.msgId = REMOVEBULLET;
						removeBulletMsg.guarantee = true;
						memcpy(removeBulletMsg.data,&(*it),sizeof(BulletInfo));
						removeBulletMsg.dataSize = sizeof(BulletInfo);
				
						for(int i = 0;i < g_session_list.size();++i)
						{
							resetMsg.sessionId = g_session_list[i].sessionId;
							resetMsg.msgOrder = ++(g_session_list[i].serverGuaranteeOrder);
							NeedToSend_Guarantee.push_back(resetMsg); 
							
							removeBulletMsg.sessionId = g_session_list[i].sessionId;
							removeBulletMsg.msgOrder = ++(g_session_list[i].serverGuaranteeOrder);
							NeedToSend_Guarantee.push_back(removeBulletMsg); 
						}

						

						GuaranteeSendFlag = true;
						
						it = bullets_list.erase(it);
						break;//next bullet;
					}
					else if(bx > 1024 || bx < 0 || bz > 1024 || bz < 0)
					  {
						  SMsg removeBulletMsg;
						  
						  removeBulletMsg.msgId = REMOVEBULLET;
						  removeBulletMsg.guarantee = true;
						  memcpy(removeBulletMsg.data,&(*it),sizeof(BulletInfo));
						  removeBulletMsg.dataSize = sizeof(BulletInfo);

						  for(int i = 0;i < g_session_list.size();++i)
						  {
							  removeBulletMsg.sessionId = g_session_list[i].sessionId;
							  removeBulletMsg.msgOrder = ++(g_session_list[i].serverGuaranteeOrder);
							  NeedToSend_Guarantee.push_back(removeBulletMsg); 
						  }
						  GuaranteeSendFlag = true;
						  it = bullets_list.erase(it);
						  hitflag = true;
						  break;//next bullet;
					  }
					 

				}
				if(!hitflag)
				++it;//next bullet;
			}

		 
 
			if(GuaranteeSendFlag || now - sendGuarantee > 1000)
			{
				sendGuarantee = now;
				startSend_Guarantee();
				GuaranteeSendFlag = false;
			}

			
			startSend();
		}

	}

void CServer::MsgHandler(const boost::system::error_code& ec,std::size_t bytes_transferred)
	{
		if(ec.value()!=0)
		{
			std::cout<<ec.message()<<std::endl;
			return;
		}

		SMsg* receivedMsg =  reinterpret_cast<SMsg*>(byteData);

		switch(receivedMsg->msgId)
		{
		case LOGIN:
			{
				Session* pS = getSessionByClient(client_address);
				if(pS!=NULL) break;//avoid login too many times

				SMsg backMsg;
				backMsg.msgId = LOGIN_SUCCEED;
				backMsg.sessionId = sessionCount++;
				Session s(SHeartBeat(client_address),backMsg.sessionId);
				g_session_list.push_back(s);

				//save the session id in the received msg 
				receivedMsg->sessionId = backMsg.sessionId;

				backMsg.dataSize = sizeof(PlayerInfo);//may be used in client
				memcpy(backMsg.data,&s.pinfo,sizeof(PlayerInfo));
 
				std::cout<<"new client login: "<<client_address.address().to_string()<<":"<<client_address.port()<<std::endl;
				
				NeedToSend.push_back(backMsg); 

				backMsg.msgId = UPDATEPLAYERS; 
				for(int i = 0;i < g_session_list.size();++i)
				{
					memcpy(backMsg.data + i * sizeof(PlayerInfo),&g_session_list[i].pinfo,sizeof(PlayerInfo));
				}
				backMsg.dataSize = g_session_list.size() ;
				
				for(int i = 0;i < g_session_list.size();++i)
				{
					backMsg.sessionId = g_session_list[i].sessionId;
					NeedToSend.push_back(backMsg); 
				}

				
			}
			break;
		case RTT:
			{
			SMsg backMsg;
				backMsg.msgId = RTT_SUCCEED;
				backMsg.time = receivedMsg->time;
				//no sending queue
				memcpy(sendBackData,&(backMsg),sizeof(SMsg));
				serverSocket->async_send_to(buffer(sendBackData,sizeof(SMsg)),client_address,
					boost::bind(&CServer::SendBackMsgHandler,this,
					boost::asio::placeholders::error,
					boost::asio::placeholders::bytes_transferred));
			}
			break;
		case HEARTBEAT:
			{

				//nothing
			}
			break;
		case LOGOUT:
			{
				playerLeaveGame(receivedMsg->sessionId);
			}
			break;
		case LISTCLIENTS:
			{
				SMsg backMsg;
				backMsg.msgId = LISTCLIENTS_SUCCEED;
				unsigned char* pt = backMsg.data;
				*reinterpret_cast<int*>(pt) = g_session_list.size();
				pt += sizeof(int);
				std::vector<Session>::iterator i;
					i = g_session_list.begin();
				for(;i != g_session_list.end(); ++i)
				{
					*reinterpret_cast<int*>(pt) = (*i).sessionId;
					pt += sizeof(int);
				} 

				NeedToSend.push_back(backMsg);


			}
		break;
		case PLAYERMOVE:
			{
				Session* ps = getSessionById(receivedMsg->sessionId);
				if(ps==NULL)break;
				PlayerInfo& info = ps->pinfo;
				memcpy(&info,receivedMsg->data,sizeof(PlayerInfo));
				SMsg backMsg;
				backMsg.msgId = UPDATEPLAYERS; 
				for(int i = 0;i < g_session_list.size();++i)
				{
					memcpy(backMsg.data + i * sizeof(PlayerInfo),&g_session_list[i].pinfo,sizeof(PlayerInfo));
				}
				backMsg.dataSize = g_session_list.size() ;
				
				for(int i = 0;i < g_session_list.size();++i)
				{
					backMsg.sessionId = g_session_list[i].sessionId;
					NeedToSend.push_back(backMsg); 
				}

			}
			break;
		case PLAYERSHOOT:
			{
				BulletInfo* binfo;
				binfo = reinterpret_cast<BulletInfo*>(receivedMsg->data);

				binfo->bulletId = ++bulletCount;
				binfo->spawnTime = CURRENTTIME

				bullets_list.push_back(BulletInfo(*binfo));

				SMsg backMsg;
				backMsg.msgId = ADDBULLET; 
				backMsg.dataSize = sizeof(BulletInfo);
 
				

				memcpy(backMsg.data,binfo,sizeof(BulletInfo));
				backMsg.guarantee = true;
				for(int i = 0;i < g_session_list.size();++i)
				{
					std::cout<<"spawn bullet "<<binfo->spawnTime<<" for "<<g_session_list[i].sessionId<<"\n";
					backMsg.sessionId = g_session_list[i].sessionId;
					backMsg.msgOrder = ++(g_session_list[i].serverGuaranteeOrder);
					NeedToSend_Guarantee.push_back(backMsg); 
				}
				GuaranteeSendFlag = true;
			}
			break;
		case ACK:
			//std::cout<<"receivedMsg ack "<<receivedMsg->msgOrder<<"\n";
			clearGuaranteeMsgs(receivedMsg);
		break;
		}
		
		if(receivedMsg->guarantee)
		{
			Session* currentSession = getSessionById(receivedMsg->sessionId);
			if(currentSession)
			{
				std::cout<<"get msg order:"<<int(receivedMsg->msgOrder)<<"from session "<<currentSession->sessionId<<" sessionOrder:"<<currentSession->clientGuaranteeOrder<<"\n";
 
				if(  receivedMsg->msgOrder <= currentSession->clientGuaranteeOrder + 1)
				{
					SMsg ackMsg;
					ackMsg.msgId = ACK;
					//std::cout<<"ack: "<<int(receivedMsg->msgOrder)<<std::endl;
					ackMsg.msgOrder = receivedMsg->msgOrder;
					ackMsg.sessionId = receivedMsg->sessionId;
					NeedToSend.push_back(ackMsg);

					//memcpy(sendBackData,&ackMsg,sizeof(SMsg));
					//serverSocket->async_send_to(buffer(sendBackData,sizeof(SMsg)),client_address,
					//boost::bind(&CServer::SendBackMsgHandler,this,
					//boost::asio::placeholders::error,
					//boost::asio::placeholders::bytes_transferred));

					if(  receivedMsg->msgOrder - currentSession->clientGuaranteeOrder ==  1)
					{
						currentSession->clientGuaranteeOrder += 1;
					}
				}
				
			}
		}
	
 
	
startReceive();

//upadte heart beat msg info
	std::vector<Session>::iterator i;
	i = g_session_list.begin();
	for(;i != g_session_list.end(); i++)
	{
		if( (*i).sessionId == receivedMsg->sessionId)
		{
			(*i).connectionInfo.lostCount = 0;
		}
	}
}

void CServer::SendBackMsgHandler(const boost::system::error_code& ec,std::size_t bytes_transferred)
{
	if(ec.value()!=0)
	{
		std::cout<<ec.message()<<std::endl;
		return;
	}
}

Session* CServer::getSessionById(int id)
{
	std::vector<Session>::iterator it;
		it = g_session_list.begin();
	for(;it!= g_session_list.end();++it)
	{
		if((*it).sessionId == id) return &(*it);
	}

	return NULL;
}

Session* CServer::getSessionByClient(ip::udp::endpoint client)
{
	std::vector<Session>::iterator it;
		it = g_session_list.begin();
	for(;it!= g_session_list.end();++it)
	{
		if((*it).connectionInfo.clientEndPoint == client) return &(*it);
	}

	return NULL;
}

void CServer::clearGuaranteeMsgs(SMsg* msg)
{
	long long msgOrder = msg->msgOrder;
	 
	std::vector<SMsg>::iterator it;
	it = NeedToSend_Guarantee.begin();
	for(;it!=NeedToSend_Guarantee.end();)
	{
		if((*it).sessionId == msg->sessionId && (*it).msgOrder <= msgOrder)
		{
			//std::cout<<"delete order "<< msgOrder<<" for session "<<msg->sessionId<<"\n";
			it = NeedToSend_Guarantee.erase(it);
		}
		else
		{
			++it;
		}
	}	
}

void CServer::startSend_Guarantee()
{
	std::vector<SMsg>::iterator it;
		it = NeedToSend_Guarantee.begin();
		for(;it!=NeedToSend_Guarantee.end();)
		{
			Session* ps = getSessionById((*it).sessionId);
			if(ps)
			{
				std::cout<<"sending guarantee msgs order: "<<(*it).msgOrder <<" for session "<<ps->sessionId<<"\n";
			memcpy(sendBackData,&(*it),sizeof(SMsg));
			serverSocket->async_send_to(buffer(sendBackData,sizeof(SMsg)),ps->connectionInfo.clientEndPoint,
				boost::bind(&CServer::SendBackMsgHandler,this,
				boost::asio::placeholders::error,
				boost::asio::placeholders::bytes_transferred)
				);
			it++;
			}
			else
			{
				it = NeedToSend_Guarantee.erase(it);
			}
		}
}

void CServer::startSend()
{
	for(int i=0;i<NeedToSend.size();i++)
	{
		memcpy(sendBackData,&NeedToSend[i],sizeof(SMsg));
		Session* pS = getSessionById(NeedToSend[i].sessionId);
		if(pS)
		{
		serverSocket->async_send_to(buffer(sendBackData,sizeof(SMsg)),pS->connectionInfo.clientEndPoint,
			boost::bind(&CServer::SendBackMsgHandler,this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred)
			);
		}
	}
		NeedToSend.clear(); 
}

std::vector<Session>::iterator CServer::playerLeaveGame(int sessionId)
{
	SMsg backMsg;
	backMsg.msgId = PLAYERLEAVE;

	std::vector<Session>::iterator i;
	i = g_session_list.begin();
	for(;i != g_session_list.end(); )
	{
		(*i).connectionInfo.lostCount += 1;
		if((*i).sessionId ==  sessionId)
		{
			std::cout<<"**client logout: session "<<(*i).sessionId<<"\n"<<
				(*i).connectionInfo.clientEndPoint.address().to_string()<<":"<<(*i).connectionInfo.clientEndPoint.port()<<std::endl;
			memcpy(backMsg.data ,&(*i).pinfo,sizeof(PlayerInfo));
			i = g_session_list.erase(i);
			break;
		}
		else
		{
			i++;
		}

	}


	backMsg.guarantee = true;
	backMsg.dataSize = sizeof(PlayerInfo);
	for(int i = 0;i < g_session_list.size();++i)
	{
		backMsg.sessionId = g_session_list[i].sessionId;
		backMsg.msgOrder = ++(g_session_list[i].serverGuaranteeOrder);
		NeedToSend_Guarantee.push_back(backMsg);
	}
	GuaranteeSendFlag = true;
	return i;//next 
}

float CServer::getRand(float start, float end)
{
	return start + (end-start) * ((float)rand())/RAND_MAX;
}

}
